parser grammar XMLParser;

options {
  language = Java;
  tokenVocab = XMLLexer;
}

@header {
package PL.prac9;
}

@members {
  // Al faltar un token, intenta una recuperación de errores llamando a esta función, por lo tanto no lanzaba la excepción y sólo mostraba el error indicándolo.
  // Sobreescribimos la función para que lance de nuevo la excepción y poder capturarla por nuestra cuenta.
  protected Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow) throws RecognitionException {
      throw new MismatchedTokenException(ttype, input);
  }
  //Variable que controlará si la regla se encuentra o no antes de un end de etiqueta.
  boolean in;
  int numerrores = 0;
  public int errores () {
    return numerrores;
    }
}
 
//Empieza el analizador sintáctico.
entrada : biblioteca EOF;

//Tag biblioteca. Contiene uno o más libros. Si ha terminado de leer los libros pero no ha terminado de leer el end, implicaría únicamente
//que no hay cierre de biblioteca. Procedemos a emitir su error.
biblioteca : BIBLIOTECA libro+ {in = true;} END_BIBLIOTECA {in = false;}; catch [RecognitionException ex] 
                                                                         {Token t = input.LT(1);
                                                                         if (in) System.out.println("falta cierre de biblioteca.");
                                                                         numerrores++;
                                                                         }
                                                                         
//Tag libro. Contiene un cuerpo explicado posteriormente. El manejo de errores es igual que en biblioteca.
libro: LIBRO cuerpo_libro {in = true;} END_LIBRO{in = false;}; catch [RecognitionException ex] 
                                                                         {Token t = input.LT(1);
                                                                         if (in) System.out.println("falta cierre de libro.");
                                                                         numerrores++;
                                                                         }
                                                                         
//Cuerpo del tag libro. Consta de un título, un autor y opcionalmente una editorial.
cuerpo_libro: titulo autor (editorial)?; catch [RecognitionException ex] {numerrores++;throw ex;}

//Tag título. Contiene una línea explicada posteriormente. El manejo de errores es igual que en biblioteca.
titulo: TITULO linea {in = true;} END_TITULO{in = false;}; catch [RecognitionException ex] {
                                                                  Token t = input.LT(1);
                                                                  if (in) System.out.println("falta cierre de título.");
                                                                  numerrores++;
                                                           }
                                                           
//Tag autor. Contiene una línea explicada posteriormente. El manejo de errores es igual que en biblioteca. Además de éstos, se le incluye
//un manejo de errores por si en vez de un autor encuentra editorial o cierre de libro, muestre un error, ya que debe haber un autor.
autor: AUTOR linea {in = true;} END_AUTOR {in = false;}; catch [RecognitionException ex] {
                                                         Token t= input.LT(1);
                                                         numerrores++;
                                                         if (in)  System.out.println("falta cierre de autor.");
                                                         else if ((t.getText().equals("<editorial>")) || (t.getText().equals("</libro>"))){                                
                                                           System.out.println ("Error, debe haber un autor.");
                                                           consumeUntil(input, END_EDITORIAL);
                                                           input.consume();
                                                         }}
                                 
//Tag editorial. Contiene una línea explicada posteriormente. El manejo de errores es igual que en biblioteca.
editorial: EDITORIAL linea {in = true;} END_EDITORIAL {in = false;}; catch [RecognitionException ex] {
                                                                        Token t = input.LT(1);
                                                                        numerrores++;
                                                                         if (in) System.out.println("falta cierre de editorial.");
                                                                      }
//Las líneas estarán formadas por una o varias palabras (suponemos que los campos no pueden estar vacíos.
linea: PALABRA+;